package com.jprocms.module.cms.service.vote;

import cn.hutool.core.date.DateUtil;
import com.jpro.framework.common.pojo.PageResult;
import com.jpro.framework.common.util.collection.CollectionUtils;
import com.jpro.framework.common.util.object.BeanUtils;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.pagehelper.page.PageMethod;
import com.jprocms.module.cms.controller.admin.vote.vo.VoteCreateReqVO;
import com.jprocms.module.cms.controller.admin.vote.vo.VoteListReqVO;
import com.jprocms.module.cms.controller.admin.vote.vo.VotePageReqVO;
import com.jprocms.module.cms.controller.admin.vote.vo.VoteUpdateReqVO;
import com.jprocms.module.cms.controller.admin.voteitem.vo.VoteItemCreateReqVO;
import com.jprocms.module.cms.controller.admin.voteitem.vo.VoteItemListReqVO;
import com.jprocms.module.cms.controller.admin.voteitem.vo.VoteItemUpdateReqVO;
import com.jprocms.module.cms.convert.PageConverter;
import com.jprocms.module.cms.convert.vote.VoteConvert;
import com.jprocms.module.cms.convert.voteitem.VoteItemConvert;
import com.jprocms.module.cms.dal.dataobject.vote.VoteDO;
import com.jprocms.module.cms.dal.dataobject.voteitem.VoteItemDO;
import com.jprocms.module.cms.dal.mysql.vote.VoteMapper;
import com.jprocms.module.cms.dal.mysql.voteitem.VoteItemMapper;
import com.jprocms.module.cms.enums.EnumFrontOperate;
import com.jprocms.module.cms.framework.seq.SnowflakeSequence;
import com.jprocms.module.cms.service.operate.OperateService;
import com.jprocms.module.cms.service.voteitem.VoteItemService;
import com.mzt.logapi.context.LogRecordContext;
import com.mzt.logapi.service.impl.DiffParseFunction;
import com.mzt.logapi.starter.annotation.LogRecord;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import static com.jpro.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.jprocms.module.cms.enums.ErrorCodeInteractConstants.VOTE_NOT_EXISTS;
import static com.jprocms.module.cms.enums.LogRecordConstants.*;

/**
 * 投票 Service 实现类
 *
 * @author jprocms
 */
@Service
@Validated
public class VoteServiceImpl implements VoteService {

    @Resource
    private VoteMapper voteMapper;
    @Resource
    private VoteItemMapper voteItemMapper;
    @Resource
    private VoteItemService voteItemService;
    @Resource
    private SnowflakeSequence snowflakeSequence;
    @Resource
    private OperateService operateService;
    @Resource
    private PageConverter pageConverter;

    @Override
    @Transactional(rollbackFor = Exception.class)
    @LogRecord(type = CMS_VOTE_TYPE, subType = CMS_VOTE_CREATE_SUB_TYPE, bizNo = "{{#vote.id}}", success = CMS_VOTE_CREATE_SUCCESS)
    public Long createVote(VoteCreateReqVO createReqVO) {
        // 插入
        VoteDO vote = VoteConvert.INSTANCE.convertMore(createReqVO);
        voteMapper.insert(vote);
        List<VoteItemCreateReqVO> items = createReqVO.getItems();
        int order = 1;
        for (VoteItemCreateReqVO item : items) {
            item.setItemSort(order);
            order += 1;
            item.setVoteId(vote.getId());
        }
        List<VoteItemDO> voteItemDOS = VoteItemConvert.INSTANCE.convertListForCreate(items);
        voteItemMapper.insertBatch(voteItemDOS);
        // 记录操作日志上下文
        LogRecordContext.putVariable("vote", vote);
        // 返回
        return vote.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @LogRecord(type = CMS_VOTE_TYPE, subType = CMS_VOTE_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", success = CMS_VOTE_UPDATE_SUCCESS)
    public void updateVote(VoteUpdateReqVO updateReqVO) {
        // 校验存在
        VoteDO oldVote = validateVoteExists(updateReqVO.getId());
        // 更新
        VoteDO updateObj = VoteConvert.INSTANCE.convertMore(updateReqVO);
        voteMapper.updateById(updateObj);
        List<VoteItemUpdateReqVO> items = updateReqVO.getItems();
        int order = 1;
        for (VoteItemUpdateReqVO item : items) {
            item.setItemSort(order);
            order += 1;
            item.setVoteId(updateObj.getId());
        }
        List<VoteItemDO> voteItemDOS = VoteItemConvert.INSTANCE.convertListForUpdate(items);
        List<VoteItemDO> toUpdateItems = voteItemDOS.stream().filter(item -> item.getId() > 0).collect(Collectors.toList());
        List<VoteItemDO> toInsertItems = voteItemDOS.stream().filter(item -> item.getId() <= 0).collect(Collectors.toList());
        toInsertItems.forEach(item -> item.setId(snowflakeSequence.nextId()));
        voteItemMapper.insertBatch(toInsertItems);
        voteItemService.batchUpdateVoteItems(toUpdateItems);
        // 记录操作日志上下文
        LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldVote, VoteUpdateReqVO.class));
        LogRecordContext.putVariable("vote", updateObj);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @LogRecord(type = CMS_VOTE_TYPE, subType = CMS_VOTE_DELETE_SUB_TYPE, bizNo = "{{#id}}", success = CMS_VOTE_DELETE_SUCCESS)
    public void deleteVote(Long id) {
        // 校验存在
        VoteDO vote = validateVoteExists(id);
        // 删除
        VoteItemListReqVO voteItemListReqVO = new VoteItemListReqVO();
        voteItemListReqVO.setVoteId(id);
        List<VoteItemDO> voteItemDOS = voteItemMapper.selectList(voteItemListReqVO);
        voteItemMapper.deleteBatchIds(CollectionUtils.convertList(voteItemDOS, VoteItemDO::getId));
        voteMapper.deleteById(id);
        // 记录操作日志上下文
        LogRecordContext.putVariable("vote", vote);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteByIds(List<Long> ids) {
        if (!org.springframework.util.CollectionUtils.isEmpty(ids)) {
            for (Long id : ids) {
                deleteVote(id);
            }
        }
    }

    private VoteDO validateVoteExists(Long id) {
        final VoteDO voteDO = voteMapper.selectById(id);
        if (voteDO == null) {
            throw exception(VOTE_NOT_EXISTS);
        }
        return voteDO;
    }

    @Override
    public VoteDO getVote(Long id) {
        return voteMapper.selectById(id);
    }

    @Override
    public List<VoteDO> getVoteList(Collection<Long> ids) {
        return voteMapper.selectBatchIds(ids);
    }

    @Override
    public PageResult<VoteDO> getVotePage(VotePageReqVO pageReqVO) {
        if (pageReqVO.getBeginDate() != null) {
            if (pageReqVO.getBeginDate().length > 0 && pageReqVO.getBeginDate()[0] != null) {
                pageReqVO.setBeginDateStart(pageReqVO.getBeginDate()[0]);
            }
            if (pageReqVO.getBeginDate().length > 1 && pageReqVO.getBeginDate()[1] != null) {
                pageReqVO.setBeginDateEnd(pageReqVO.getBeginDate()[1]);
            }
        }
        pageReqVO.setNow(Date.from(DateUtil.date().toInstant()));
        IPage<VoteDO> voteDOIPage = voteMapper.selectPage(pageReqVO, pageReqVO.getOrderItems(), new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()));
        return pageConverter.page(voteDOIPage);
    }

    @Override
    public List<VoteDO> getVoteList(VoteListReqVO reqVO) {
        reqVO.setNow(Date.from(DateUtil.date().toInstant()));
        return PageMethod.offsetPage(reqVO.getOffset(), reqVO.getCount(), false).doSelectPage(() -> voteMapper.selectList(reqVO, reqVO.getOrderItems()));
    }

    @Transactional(rollbackFor = Exception.class)
    public void vote(Long id, List<Long> itemIds, @Nullable Long userId, String ip, long cookie) {
        operateService.insertOperate(id, EnumFrontOperate.VOTE, cookie, ip, userId);
        voteMapper.vote(id);
        voteItemMapper.vote(id, itemIds);
    }

}
